/*
 * Copyright (c) 2010, Katholieke Universiteit Leuven
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of the Katholieke Universiteit Leuven nor the names of
 *       its contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <arpa/inet.h>

#include "../api/runtime_control.h"
#include "net.h"

static uint8_t ibuffer[LOOCI_COMMANDS_BUFSIZE + sizeof(struct command_request)];
static struct command_request * request = (struct command_request *) ibuffer;
static uint8_t obuffer[LOOCI_COMMANDS_BUFSIZE + sizeof(struct command_reply)];
static struct command_reply * reply = (struct command_reply *) obuffer;

static bool simple_command(uint8_t cmd, uint8_t componentID, char * nodeID) {
  request->cmd = cmd;
  request->payload[0] = componentID;
  size_t oplen = 1;
  bool success = send_command(nodeID, request, 1, reply, &oplen);
  if(success == false || oplen != 1) {
    fprintf(stderr, "Something wrong while sending simple command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_deactivate(uint8_t componentID, char * nodeID) {
  return simple_command(COMMAND_DEACTIVATE, componentID, nodeID);
}

bool looci_activate(uint8_t componentID, char * nodeID) {
  return simple_command(COMMAND_ACTIVATE, componentID, nodeID);
}

bool looci_reset_wirings(uint8_t componentID, char * nodeID) {
  return simple_command(COMMAND_RESET_WIRINGS, componentID, nodeID);
}

static bool un_wire_local_common(uint8_t cmd, uint8_t interfaceEvent, uint8_t sourceComponentID, 
    uint8_t receptacleEvent, uint8_t destComponentID, char * nodeID) {
  request->cmd = cmd;
  request->payload[0] = interfaceEvent;
  request->payload[1] = sourceComponentID;
  request->payload[2] = destComponentID;
  size_t oplen = 1;
  bool success = send_command(nodeID, request, 3, reply, &oplen);
  if(success == false || oplen != 1) {
    fprintf(stderr, "Something wrong while sending (un)wire local command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_wire_local(uint8_t interfaceEvent, uint8_t sourceComponentID, 
    uint8_t receptacleEvent, uint8_t destComponentID, char * nodeID) {
  return un_wire_local_common(COMMAND_WIRE_LOCAL, interfaceEvent, sourceComponentID, receptacleEvent, destComponentID, nodeID);
}

bool looci_unwire_local(uint8_t interfaceEvent, uint8_t sourceComponentID,
    uint8_t receptacleEvent, uint8_t destComponentID, char * nodeID) {
  return un_wire_local_common(COMMAND_UNWIRE_LOCAL, interfaceEvent, sourceComponentID, receptacleEvent, destComponentID, nodeID);
}

static bool un_wire_to_common(uint8_t cmd, uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, char * destNodeID) {
  request->cmd = cmd;
  request->payload[0] = interfaceEvent;
  request->payload[1] = sourceComponentID;
  if(convert_node_id(destNodeID, request->payload + 2)==false) {
    return false;
  }
  size_t oplen = 1;
  bool success = send_command(sourceNodeID, request, 18, reply, &oplen);
  if(success == false || oplen!=1) {
    fprintf(stderr, "Something wrong while sending (un)wire to command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_wire_to(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, char * destNodeID) {
  return un_wire_to_common(COMMAND_WIRE_REMOTE_TO, interfaceEvent, sourceComponentID, sourceNodeID, destNodeID);
}

bool looci_unwire_to(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, char * destNodeID) {
  return un_wire_to_common(COMMAND_UNWIRE_REMOTE_TO, interfaceEvent, sourceComponentID, sourceNodeID, destNodeID);
}

static bool un_wire_from_common(uint8_t cmd, uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, 
    uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  request->cmd = cmd;
  request->payload[0] = interfaceEvent;
  if(convert_node_id(sourceNodeID, request->payload + 1)==false) {
    return false;
  }
  request->payload[17] = sourceComponentID;
  request->payload[18] = destComponentID;
  size_t oplen = 1;
  bool success = send_command(destNodeID, request, 19, reply, &oplen);
  if(success == false || oplen!=1) {
    fprintf(stderr, "Something wrong while sending (un)wire from command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_wire_from(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, 
    uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  return un_wire_from_common(COMMAND_WIRE_REMOTE_FROM, interfaceEvent, sourceComponentID, sourceNodeID, receptacleEvent, destComponentID, destNodeID);
}

bool looci_unwire_from(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID, 
    uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  return un_wire_from_common(COMMAND_UNWIRE_REMOTE_FROM, interfaceEvent, sourceComponentID, sourceNodeID, receptacleEvent, destComponentID, destNodeID);
}

static bool un_wire_from_all_common(uint8_t cmd, uint8_t interfaceEvent, uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  request->cmd = cmd;
  request->payload[0] = interfaceEvent;
  request->payload[1] = destComponentID;
  size_t oplen = 1;
  bool success = send_command(destNodeID, request, 2, reply, &oplen);
  if(success == false || oplen!=1) {
    fprintf(stderr, "Something wrong while sending (un)wire from all command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_wire_from_all(uint8_t interfaceEvent, uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  return un_wire_from_all_common(COMMAND_WIRE_REMOTE_FROM_ALL, interfaceEvent, receptacleEvent, destComponentID, destNodeID);
}

bool looci_unwire_from_all(uint8_t interfaceEvent, uint8_t receptacleEvent, uint8_t destComponentID, char * destNodeID) {
  return un_wire_from_all_common(COMMAND_UNWIRE_REMOTE_FROM_ALL, interfaceEvent, receptacleEvent, destComponentID, destNodeID);
}

static bool un_wire_to_all_common(uint8_t cmd, uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID){
  request->cmd = cmd;
  request->payload[0] = interfaceEvent;
  request->payload[1] = sourceComponentID;
  size_t oplen = 1;
  bool success = send_command(sourceNodeID, request, 2, reply, &oplen);
  if(success == false || oplen!=1) {
    fprintf(stderr, "Something wrong while sending (un)wire to all command or reading the reply\r\n");
    return false;
  }
  return (bool) reply->payload[0];
}

bool looci_wire_to_all(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID) {
  return un_wire_to_all_common(COMMAND_WIRE_REMOTE_TO_ALL, interfaceEvent, sourceComponentID, sourceNodeID);
}

bool looci_unwire_to_all(uint8_t interfaceEvent, uint8_t sourceComponentID, char * sourceNodeID) {
  return un_wire_to_all_common(COMMAND_UNWIRE_REMOTE_TO_ALL, interfaceEvent, sourceComponentID, sourceNodeID);
}
